Fix the implementation of BlowFish to solve bugs concerning NUL bytes truncating strings#21675
Fix the implementation of BlowFish to solve bugs concerning NUL bytes truncating strings#21675LamentXU123 wants to merge 10 commits intophp:masterfrom
Conversation
| zend_long cost = PHP_PASSWORD_BCRYPT_COST; | ||
|
|
||
| if (memchr(ZSTR_VAL(password), '\0', ZSTR_LEN(password))) { | ||
| if (zend_str_has_nul_byte(password)) { |
There was a problem hiding this comment.
nit: I also refactor this by the way.
|
Shouldn't that be rather done in |
It is before, see the initial fix in 6f14bee Actually I think that'd be better. Anyway, lets wait for code owners. |
|
Can we not fix the blowfish implementation? It seems like it requires providing the length of the string all the way down to |
Blowfish do support null bytes, but in php implementation it specifically doesn't. Any password with NUL will fail when hashing by using bugs with NUL bytes is inevitable since PHP is based on C string. But in this case, I think directly rejecting all inputs containing NUL bytes will be safer and always correct. |
Sure, but we "own" the implementation of BF, so why not actually fix the implementation? I'm happy to reject them as a bug fix that is backported, but in the long run we should fix the implementation. |
In the long run we should. But for now, password_hash does not accept password containing NUL, so fixing the implementation would create a BC break on this which requires a RFC. I would be happy to write such RFC in the future but I think this bug could be fix before we get it passed. |
|
I don't think fixing the implementation to be correct is a BC break. |
That'd be good. I will work on this hours later. I assume that the best practice here would be passing the correct length throughout the implementation so the strings will not be cut by NUL |
Correct :) |
| Z_PARAM_STRING(str, str_len) | ||
| Z_PARAM_STRING(salt_in, salt_in_len) |
There was a problem hiding this comment.
We may want these to be Z_PARAM_PATH if we use the system crypt. Or Maybe it should indeed always be PATH so that the behaviour is consistent with the system API...
There was a problem hiding this comment.
We may want these to be Z_PARAM_PATH
I implement the bcrypt() to reject NUL byte, and make password_* not to.
Not sure about this change.
Co-authored-by: Gina Peter Banyard <girgias@php.net>
There was a problem hiding this comment.
This file is a vendored library and must not be touched.
It's not BlowFish, it's Bcrypt. And no, most Bcrypt implementation don't support NULs. |
|
The idiotic thing about how PHP implements password hashing is that it doesn't always use the builtin bcrypt implementation: it can defer to crypt_r which doesn't support NUL bytes. So if you want to fix this you'll need to ditch that other code path but that runs into political issues about "bundling too much code". |
|
I guess the idea was that crypt implementation would be more secure but not sure if there's much value in it. |
|
I'm also not sure if there's much value in allowing nul bytes in password. I think the initial idea of not allowing in the verify was more reasonable. That was sort of the plan when we were handling GHSA-h746-cjrr-wfmr - prohibit the hashing as low security issue and add warning for verify in master. |
|
I don't have strong opinions on both solutions, but I would revert back to the initial fix I guess if we are following GHSA-h746-cjrr-wfmr plus: Is this issue worth a separate security entry? I mean that |
Please explain the impact you're seeing. I'm seeing none and I don't think this change is necessary either. For |
Now, since including \0 in
password_hashis not allowed in PASSWORD_BCRYPT. It is reasonable to add checkers that, makes input that includes \0 inpassword_verifydirectly fails.This PR align
password_verify()withpassword_hash()for bcrypt by rejecting passwords containing NUL bytes before verification.Because bcrypt treats passwords as NUL-terminated C strings, this avoids ambiguous verification behavior for inputs such as "foo\0bar". A regression test is included.
Fix #21673
To be short: password encrypted by BYCRYPT should never includes \0. So directly rejecting it is always correct. It also avoids ambiguous verification where something like this happens: